package moe.dao.pool;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class LRUFixedSizeMap<K, V> implements Map<K, V> {
	private Map<K, V> internalMap;
	private Map<K, Integer> accessCountMap;
	private int capacity, recentlyIndex = 0;

	public LRUFixedSizeMap(int capacity) {
		this.capacity = capacity + 1;
		internalMap = new HashMap<K, V>(capacity);
		accessCountMap = new HashMap<K, Integer>(capacity);
	}

	public int size() {
		return internalMap.size();
	}

	public boolean isEmpty() {
		return internalMap.isEmpty();
	}

	public boolean containsKey(Object key) {
		return internalMap.containsKey(key);
	}

	public boolean containsValue(Object value) {
		return internalMap.containsValue(value);
	}

	@SuppressWarnings("unchecked")
	public V get(Object key) {
		V value = internalMap.get(key);
		if (value != null) {
			recentlyUsed((K) key);
		}
		return value;
	}

	public V put(K key, V value) {
		if (key == null || value == null) {
			return null;
		}
		V result = doPut(key, value);
		V old = null;
		if (isOverfull()) {
			old = doWeedOut();
		}
		if (old != null) {
			result = old;
		}
		return result;
	}

	public V remove(Object key) {
		accessCountMap.remove(key);
		return internalMap.remove(key);
	}

	public void clear() {
		accessCountMap.clear();
		internalMap.clear();
	}

	private boolean isOverfull() {
		return size() >= capacity;
	}

	private V doWeedOut() {
		Set<K> keys = accessCountMap.keySet();
		int min = Integer.MAX_VALUE;
		K weedKey = null;
		for (K key : keys) {
			int i = accessCountMap.get(key);
			if (min > i) {
				min = i;
				weedKey = key;
			}
		}
		return this.remove(weedKey);
	}

	private V doPut(K key, V value) {
		recentlyUsed(key);
		return internalMap.put(key, value);
	}

	private void recentlyUsed(K key) {
		accessCountMap.put(key, ++recentlyIndex);
	}

	@Override
	public boolean equals(Object o) {
		return internalMap.equals(o);
	}

	@Override
	public int hashCode() {
		return internalMap.hashCode();
	}

	public void putAll(Map<? extends K, ? extends V> m) {
		throw new UnsupportedOperationException();
	}

	public Set<K> keySet() {
		throw new UnsupportedOperationException();
	}

	public Collection<V> values() {
		throw new UnsupportedOperationException();
	}

	public Set<java.util.Map.Entry<K, V>> entrySet() {
		throw new UnsupportedOperationException();
	}

}
